//download file from website
import fs from 'fs'
import path from 'path'
import EventEmitter from 'events'
import fetch from 'node-fetch'
import { mkdir,existsFile,deldir } from './fileutil.js'
import {collapse} from './ffmpeg.js'
import { humanSize } from './common.js'
import {getRandom} from './useragent.js'

export default class download extends EventEmitter{

  /**
   * class constructor
   * @param {String} url download url
   * @param {String} storeFilePath store filepath after download success
   */
  constructor(url,storeFilePath,options) { 
    super();
    this.url = url;
    this.storeFilePath = typeof storeFilePath == 'object' ? '' : storeFilePath;
    this.options = typeof storeFilePath == 'object' ? storeFilePath : options || {};
    return this;
  }
  setOptions(options) {
    this.options = options;
    return this;
  }
  setUrl(url) {
    this.url = url;
    return this;
  }
  setRandomUserAgent() {
    this.options = Object.assign(this.options || {}, {
      'User-Agent' : getRandom()
    })
    return this;
  }
  async request() {
    return await fetch(this.url, this.options);
  }
  //get json data from url
  async json() {
    const res = await this.request()
    return await res.json();
  }
  //get html from url
  async html() {
    const res = await this.request();
    return await res.text();
  }
  //download file
  async download() {
    if (!this.storeFilePath) {
      throw new Error('download - storeFilePath : parameter is not found')
    }
    let that = this;
    return new Promise(async (r, j) => {
      const response = await that.request();
      let fileSize = response.headers.get('content-length');
      mkdir(path.dirname(this.storeFilePath));
      let cs = fs.createWriteStream(this.storeFilePath);
      let sendCount = 0;
      let lastTime = +new Date();
      let start = lastTime;
      let lastCount = 0;
      response.body.on('data', d => {
        sendCount += d.length;
        let cur = +new Date();
        let diff = Math.max(cur - lastTime, 1);
        let speed = '';
        if (diff > 1000) {//unreach 1000 ms.
          speed = humanSize((sendCount - lastCount) / diff * 1000) + '/s';  
          lastCount = sendCount;
          lastTime = cur;
        }
        that.emit('progress', {
          total: fileSize,
          send: sendCount,
          speed : speed,
          totalStr: humanSize(fileSize),
          sendStr: humanSize(sendCount)
        });
      });
      response.body.on('error', err => {
        j(err)
      });
      response.body.on('end', () => {
        //calculate average download speed
        let cur = +new Date();
        let diff = cur - start;
        let averageSize = humanSize(fileSize / diff * 1000)+'/s';
        r({
          total: fileSize,
          speed: averageSize,
          time : diff / 1000,
          totalStr : humanSize(fileSize)
        });
      });
      response.body.pipe(cs);
    })
  }
  /**
   * download m3u8 and ts file,collapse ts to mp4
   * @param {Number} threads default 1.
   */
  async downloadM3u8(threads=1) {
    //get m3u8 file 
    let that = this;
    let ins = await this.request();
    let m3u8Content = await ins.text();
    
    let fileList = [];
    let list = m3u8Content.split('\n').filter(t => {
      return t[0] != '#';
    }).filter(t => {
      return t != '';
    }).map(t => {
      // let tspath = path.normalize(path.join(folder, path.basename(t)));
      let realName = t.split('?')[0];
      let tspath = path.basename(realName);
      tspath = tspath.replace(/\\/g, '/');
      fileList.push(tspath);
      return t.indexOf('http') > -1 ? t :
        path.dirname(that.url) + '/' + t;
    });
    //create ts folder ,and download all 
    let tsFolderName = path.basename(that.storeFilePath,path.extname(that.storeFilePath)) + '_m3u8';
    let tsFolder = path.join(path.dirname(that.storeFilePath), tsFolderName);
    mkdir(tsFolder);
    for (let i = 0; i < fileList.length;i+=threads){
      let downloadArr = fileList.slice(i, i + threads);
      //
      let downloadFn = downloadArr.map((t,m) => {
        return (function (tsFileName,index) {
          return new Promise(async (r, j) => {
            let tsFilePath = path.join(tsFolder, tsFileName);
            if (!existsFile(tsFilePath)) {
              let tempIns = await fetch(list[index], that.options);//use options of m3u8
              tempIns.body.on('end', () => {
                r();
              });
              tempIns.body.on('error', err => {
                j(err);
              });
              tempIns.body.pipe(fs.createWriteStream(tsFilePath));
            } else {
              r();
            }
          });
        })(t,m);
      });

      //start download
      await Promise.all(downloadFn);

    }

    //start collapse ts file to mp4.
    //write concat list to collapseFile
    let collapseFile = path.join(tsFolder, 'index.txt');

    fs.writeFileSync(collapseFile, fileList.map(t => {
      return 'file ' + t + '';
    }).join('\r\n'));
    await collapse(collapseFile, that.storeFilePath);
    //remove ts folder
    deldir(tsFolder);
    return true;
  }
  //send data by post method
  async post(data) {
    this.options.method = 'POST';
    this.options.body = JSON.stringify(data);
    this.options.headers = Object.assign(this.options.headers || {}, {
      'Content-Type': 'application/json'
    });
    let response = await this.request();
    let rst = await response.json();
    return rst;
  }
}

